package database.dbObjects;

import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Vector;

import database.dateTime.DBDateTime;

import main.Main;
import objects.Event;

public interface IDBEvent extends IDBObject {
	
	public class DBEvent extends DBObject {
		
		/*
		 * methods are final for optimization of the code (by the compiler)
		 * it is not possible anymore to inherit it after this declaration 
		 */
		
		public static final int lengthDateTimeFormat = 15;
		public static final int maxLengthSummary = 200;
		
		
		public void init() throws Exception {
			
			createTable();
			createTriggers();
		}
		
		private final void createTable() throws Exception {
			
			String query = "create table Event( " +
						   "Start char(" + lengthDateTimeFormat + ") not null, " +
						   "End char(" + lengthDateTimeFormat + "), " +
						   "Summary varchar(" + maxLengthSummary + ") not null, " +
						   "IsDeadline boolean not null, " +
						   "WarningDate char(" + lengthDateTimeFormat + "), " +
						   "MoreInformation varchar(" + maxLengthInfo + "), " +
						   "primary key (Start, Summary) " +
						       "on conflict abort);";
	        
			executeCreation(query);
		}
		
		private final void createTriggers() throws Exception {
			
			String query;
			query = "create trigger update_event_keys after update of Start, Summary on Event " +
			   	    "begin " +
			   	    	"update Lesson set Start = new.Start, Summary = new.Summary " +
			   	    		"where ((Start = old.Start) and (Summary = old.Summary)); " +
				    	"update RelTaskEvent set Event_Start = new.Start, " +
				    		"Event_Summary = new.Summary " +
				    		"where ((Event_Start = old.Start) and " +
				    		"(Event_Summary = old.Summary)); " +
				    "end;";
			executeCreation(query);
			
			query = "create trigger delete_event_keys before delete on Event " +
					"begin " +
						"delete from Lesson " +
							"where ((Start = old.Start) and (Summary = old.Summary)); " +
	    	   			"delete from RelTaskEvent " +
	    	   				"where ((Event_Start = old.Start) and " +
	    	   				"(Event_Summary = old.Summary)); " +
	    	   		"end;";
			executeCreation(query);
		}
		
		public final boolean add(String start, String end, String summary, 
				Boolean isDeadline, String warn, String moreInfo) throws Exception {
			
			checkConsistance(start, end, summary, isDeadline, warn, moreInfo);
			if (moreInfo == null) {	moreInfo = "Nothing."; }
			
			Vector<Object> v = new Vector<Object>(6);
			v.add(0, start);
			v.add(1, end);
			v.add(2, summary);
			v.add(3, isDeadline);
			v.add(4, warn);
			v.add(5, moreInfo);
			
			return super.add("Event", v);
		}
		
		public final boolean delete(String start, String summary) throws Exception {
			
			checkKeys(start, summary);
			
			Vector<Object> keys = new Vector<Object>(2);
			keys.add(0, start);
			keys.add(1, summary);
			
			return super.delete("Event", keyColumns, keys);
		}
		
		public final boolean updateStart(String start, String summary, 
				String newStart) throws Exception {
			
			checkKeys(start, summary);
			checkStart(newStart);
			
			Vector<Object> keys = new Vector<Object>(2);
			keys.add(0, start);
			keys.add(1, summary);
			
			return super.update("Event", keyColumns, keys, "Start", newStart);
		}
		
		public final boolean updateEnd(String start, String summary, 
				String newEnd) throws Exception {
			
			checkKeys(start, summary);
			checkEnd(start, newEnd);
			
			Vector<Object> keys = new Vector<Object>(2);
			keys.add(0, start);
			keys.add(1, summary);
			
			return super.update("Event", keyColumns, keys, "End", newEnd);
		}
		
		public final boolean updateSummary(String start, String summary, 
				String newSummary) throws Exception {
			
			checkKeys(start, summary);
			checkSummary(newSummary);
			
			Vector<Object> keys = new Vector<Object>(2);
			keys.add(0, start);
			keys.add(1, summary);
			
			return super.update("Event", keyColumns, keys, "Summary", newSummary);
		}
		
		public final boolean updateIsDeadline(String start, String summary, 
				Boolean isDeadline) throws Exception {
			
			checkKeys(start, summary);
			checkIsDeadline(isDeadline);
			
			Vector<Object> keys = new Vector<Object>(2);
			keys.add(0, start);
			keys.add(1, summary);
			
			return super.update("Event", keyColumns, keys, "IsDeadline", isDeadline);
		}
		
		public boolean updateWarningDate(String start, String summary, 
				String newWarn) throws Exception {
			
			checkKeys(start, summary);
			checkWarningDate(start, newWarn);
			
			Vector<Object> keys = new Vector<Object>(2);
			keys.add(0, start);
			keys.add(1, summary);
			
			return super.update("Event", keyColumns, keys, "WarningDate", newWarn);
		}

		public final boolean updateMoreInformation(String start, String summary, 
				String newInfo) throws Exception {
			
			checkKeys(start, summary);
			checkMoreInformation(newInfo);
			if (newInfo == null) { newInfo = "Nothing."; }
			
			Vector<Object> keys = new Vector<Object>(2);
			keys.add(0, start);
			keys.add(1, summary);
			
			return super.update("Event", keyColumns, keys, "MoreInformation", newInfo);
		}
		
		public Event getEvent(String start, String summary) throws Exception {
			// Returns the requested object or null if nothing was found
			
			Statement stat = Main.connection.createStatement();
			ResultSet rs = stat.executeQuery("select * from Event " +
									"where ((Start = '" + start + "') and " +
									"(Summary = '" + summary + "'));");
			if (rs.next()) {
				Event e = new Event(rs.getString("Start"), 
						rs.getString("End"), rs.getString("Summary"), 
						rs.getBoolean("IsDeadline"), rs.getString("WarningDate"), 
						rs.getString("MoreInformation"));
				rs.close();
				return e;
			}
			rs.close();
			return null;
		}
		
		public Vector<Event> getAllEvents(boolean withDeadlines) throws Exception {
			
			Statement stat = Main.connection.createStatement();
			String query = "select * from Event " + 
					"where (not exists (select * from Lesson " +
						"where ((Event.Start = Lesson.Start) and " +
							"(Event.Summary = Lesson.Summary)";
			if (!withDeadlines) {
				query += " and (IsDeadline = 0)";
			}
			query += ")));";
			ResultSet rs = stat.executeQuery(query);
			
			Vector<Event> v = new Vector<Event>();
			int counter = 0;
			while (rs.next()) {
				v.add(counter, new Event(rs.getString("Start"), 
						rs.getString("End"), rs.getString("Summary"), 
						rs.getBoolean("IsDeadline"), rs.getString("WarningDate"), 
						rs.getString("MoreInformation")));
				counter++;
			}
			rs.close();
			return v;
		}
		
		public Vector<Event> getAllDeadlines() throws Exception {
			
			Statement stat = Main.connection.createStatement();
			
			ResultSet rs = stat.executeQuery("select * from Event " +
											 "where (IsDeadline = 1);");
			
			Vector<Event> v = new Vector<Event>();
			int counter = 0;
			while (rs.next()) {
				v.add(counter, new Event(rs.getString("Start"), 
						rs.getString("End"), rs.getString("Summary"), 
						rs.getBoolean("IsDeadline"), rs.getString("WarningDate"), 
						rs.getString("MoreInformation")));
				counter++;
			}
			rs.close();
			return v;
		}
		
		protected final void checkConsistance(String start, String end, String summary, 
				Boolean isDeadline, String warn, String moreInfo) throws Exception {
			
			checkStart(start);
			checkEnd(start, end);
			checkSummary(summary);
			checkIsDeadline(isDeadline);
			checkWarningDate(start, warn);
			checkMoreInformation(moreInfo);
		}
		
		public final void checkKeys(String start, String summary) throws Exception {
			
			checkStart(start);
			checkSummary(summary);
		}
		
		protected final void checkStart(String start) throws Exception {
			
			if (start == null) {
				throw (new Exception("Start needed!"));
			}
			if (start.length() != lengthDateTimeFormat) {
				throw (new Exception("Start has to be exact " + 
						lengthDateTimeFormat + " characters long!"));
			}
		}
		
		protected final void checkEnd(String start, String end) throws Exception {
			
			if (end != null) {
				if (end.length() != lengthDateTimeFormat) {
					throw (new Exception("End has to be exact " + 
							lengthDateTimeFormat + " characters long!"));
				}
				DBDateTime s = DBDateTime.create(start);
				DBDateTime e = DBDateTime.create(end);
				if (!(s.before(e) || start.equals(end))) {
					throw (new Exception("Start can't be after End!"));
				}
			}
		}
		
		protected final void checkSummary(String summary) throws Exception {
			
			if (summary == null) { throw (new Exception("Summary needed!")); }
			if (summary.length() < 1) {
				throw (new Exception("Summary has to be at least 1 character long!"));
			}
			if (summary.length() > maxLengthSummary) {
				throw (new Exception("Summary exceeds maximum length (" +
						maxLengthSummary + " characters)!"));
			}
		}
		
		protected final void checkIsDeadline(Boolean isDeadline) throws Exception {
			
			if (isDeadline == null) {
				throw (new Exception("Boolean IsDeadline needed!"));
			}
		}
		
		protected final void checkWarningDate(String start, String warn) throws Exception {
			
			if (warn != null) {
				if (warn.length() != lengthDateTimeFormat) {
					throw (new Exception("WarningDate has to be exact " + 
							lengthDateTimeFormat + " characters long!"));
				}
				DBDateTime s = DBDateTime.create(start);
				DBDateTime w = DBDateTime.create(warn);
				if (!w.before(s)) {
					throw (new Exception("WarningDate has to come before Start!"));
				}
			}
		}
		
		protected final void checkMoreInformation(String moreInfo) throws Exception {
			
			if ((moreInfo != null) && (moreInfo.length() > maxLengthInfo)) {
				throw (new Exception("MoreInformation exceeds maximum length (" +
						maxLengthInfo + " characters)!"));
			}
		}
		
		
		protected final static Vector<String> keyColumns = new Vector<String>(2);
		static {
			keyColumns.add(0, "Start");
			keyColumns.add(1, "Summary");
		}
		private final int maxLengthInfo = 1000;
		
	}
	
}
